export function isString(val:any){
    return typeof(val) === "string"
}
export function isBoolean(val:any){
    return typeof(val) === 'boolean'
}
export function isNumber(val:any){
    return typeof(val) === 'number'
}
export function isObject(val:any){
    return typeof(val) === "object"
}
export function isDate(value:any){
    return value && value instanceof Date
}
export function isFunction(val:any){
    return typeof(val) === 'function'
}
export function isUndefined(val:any){
    return typeof(val) === 'undefined'
}
/**
 * 判断是否为null,此函数undefined也表示为null
 * @param val -需要判断的对象
 * @returns
 */
export function isNull(val:any){
    return null === val || typeof(val) === 'undefined'
}

export function isEmptyArray(list?:any[]){
    return !list || list.length == 0
}

export function type(val:any) {
    let result
    if(val === null){
        result = 'null'
    }else{
        const valType = typeof(val)
        if(valType === 'object'){
            if(Array.isArray(val)){
                result = 'array'
            }else if(val instanceof Date){
                result = 'date'
            }else{
                result = valType
            }
        }else{
            result = valType
        }
    }
    return result
}

const NOT_ATOMIC_TYPES = ['object','array']

export function isAtomic(val:any) {
    const valType = type(val)
    return isAtomicType(valType)
}

export function  isAtomicType(valType:string) {
    return NOT_ATOMIC_TYPES.indexOf(valType) == -1
}

export interface CopyConf{
    copyNull:boolean,
    copyUndefined:boolean
}
const DEFAULT_COPY_CONF:CopyConf = {copyNull:true,copyUndefined:true}
function _depthCopyProps(conf:CopyConf, dest:any,srcData:any){
    if(dest && srcData){
        for(const key in srcData){
            const srcVal = srcData[key]
            const srcValType = type(srcVal)
            if(isAtomicType(srcValType)){
                if(srcValType === 'undefined'){
                    if(conf.copyUndefined !== false){
                        dest[key] = srcVal
                    }
                }else if(srcValType === 'null'){
                    if(conf.copyNull !== false){
                        dest[key] = srcVal
                    }
                }else {
                    dest[key] = srcVal
                }
            }else{
                let destVal:any = dest[key]
                const destValType = type(destVal)
                if(destValType !== srcValType){
                    destVal = srcValType === 'array' ? [] : {}
                    dest[key] = destVal
                }
                _depthCopyProps(conf,destVal,srcVal)
            }
        }
    }
    return dest;
}


function _depthCopyPropsIfNotExists(dest:any,srcData:any){
    if(dest && srcData){
        for(const key in srcData){
            let destVal:any = dest[key]
            const destValType = type(destVal)
            if('undefined' != destValType && 'null' != destValType && isAtomicType(destValType)){//如果目标属性不为null并且是原子属性，则跳过
                continue;
            }

            const srcVal = srcData[key]
            const srcValType = type(srcVal)

            if(isAtomicType(srcValType)){
                if(srcValType === 'undefined' || 'null' === srcValType){//源属性为null,不处理
                    continue;
                }else {
                    dest[key] = srcVal
                }
            }else{
                if(destValType !== srcValType){
                    destVal = srcValType === 'array' ? [] : {}
                    dest[key] = destVal
                }
                _depthCopyPropsIfNotExists(destVal,srcVal)
            }
        }
    }
    return dest;
}

/**
 * 深度复制属性
 * @param dest -目标对象
 * @param srcList -需要复制的对象
 * @returns
 */
export function depthCopyProps(dest:any,...srcList:any[]):any{
    return depthCopyPropsWithConf(DEFAULT_COPY_CONF,dest,...srcList)
}

/**
 * 为目标对象设置默认值
 * @param dest -目标对象
 * @param srcList -默认值
 * @returns
 */
export function setDefaultValues(dest:any,...srcList:any[]){
    if(dest && srcList && srcList.length > 0){
        srcList.forEach(item=>{
            _depthCopyPropsIfNotExists(dest,item)
        })
    }
    return dest
}

export const depthCopyPropsIf = setDefaultValues

export function depthCopyPropsWithConf(conf:CopyConf, dest:any,...srcList:any[]){
    if(dest && srcList && srcList.length > 0){
        for(let i = 0;i<srcList.length ; i++){
            const item = srcList[i]
            _depthCopyProps(conf,dest,item)
        }
    }
    return dest
}

/**
 * 克隆
 * @param data -克隆的对象
 * @returns
 */
export function clone(data:any){
    let result
    const dataType = type(data)
    if(dataType === 'array'){
        result = depthCopyPropsWithConf(DEFAULT_COPY_CONF,[],data)
    }else if(dataType === 'object'){
        result = depthCopyPropsWithConf(DEFAULT_COPY_CONF,{},data)
    }else{
        result = data
    }
    return result
}

/**
 * 序列执行
 * @param fns -需要执行的函数
 * @param target -目标对象
 * @param args -参数
 * @param assert -断言
 * @returns
 */
export async function queueInvoke(fns:undefined | Function | Function[],target:any,args:any[],assert?:(value:any)=>boolean | Promise<boolean>){
    if(!fns){
        return
    }
    if(Array.isArray(fns)){
        const len = fns.length;
        let result
        for(let i=0;i<len;i++){
            const item = fns[i]
            result = await item.apply(target,args)
            if(assert && !(await assert(result))){
                return result
            }
        }
        return result
    }else{
        return fns.apply(target,args)

    }
}

export function invoke(fns:undefined | Function | Function[],target:any,args:any[]){
    if(!fns){
        return
    }
    let result
    if(Array.isArray(fns)){
        for(let i=0;i<fns.length;i++){
            const item = fns[i]
            result = item.apply(target,args)
        }
    }else{
        result = fns.apply(target,args)
    }
    return result
}



export const CLASS_INST_MAP:Record<any,Record<string,any>> = {}

export interface EmptyConstructor{
    new (...args:any[]):unknown
}

export function getClassDefaultInstance(clazz:EmptyConstructor,factory?:()=>any,key:string="_"){
    const clazzKey:any = clazz
    let instMap = CLASS_INST_MAP[clazzKey]
    if(!instMap){
        instMap = {}
        CLASS_INST_MAP[clazzKey] = instMap
    }
    let inst = instMap[key]
    if(!inst){
        inst = factory ? factory() : new clazz()
        instMap[key] = inst
    }
    return inst
}

export function getClassDefaultValue(clazz:EmptyConstructor, propertyKey:string,instFactory?:()=>any,instKey?:string){
    let inst = getClassDefaultInstance(clazz,instFactory,instKey)
    const result = inst[propertyKey]
    return result
}

